package flash.sound.audio {
  import flash.events.Event;
  import flash.events.SamplesCallbackEvent;
  import flash.media.Sound;
  import flash.media.SoundChannel;
  import flash.sound.audio.buffer.AudioBuffer;
  import flash.sound.audio.buffer.IAudioBuffer;
  import flash.utils.ByteArray;
  
  public class WaveOut extends AudioBuffer {
        
    private var buffers: Array = [];
    
    private var sound: Sound = new Sound();
    
    private var bufferSize: int = 1024;
    
    private var channel: SoundChannel;
    
    public var isPlaying: Boolean = false;
    
    public function WaveOut() {           
      sound.addEventListener("samplesCallback", getBuffer);              
    }
    
    private function createPrimaryBuffer(): void {
      data.position = 0;
      for (var i: int = 0; i < bufferSize; i++) {      	
        data.writeFloat(0);
        data.writeFloat(0);
      } 
    }
    
    public function play(): void {
   	  if (!isPlaying) {
   	  	isPlaying = true;
      	channel = sound.play();
      	channel.addEventListener(Event.SOUND_COMPLETE, complete);      	
      }        
    }

    public function stop(): void {
      if (channel && isPlaying) {      	
        channel.stop();
        isPlaying = false;
      }
    }

    private function complete(e: Event): void {      
      trace('complete');
      if (channel) {
        channel.removeEventListener(Event.SOUND_COMPLETE, complete);
      }
    }
    
    private function getBuffer(event: SamplesCallbackEvent): void {
      createPrimaryBuffer();      
      mixSecondaryBuffers(event.position);           
      sound.samplesCallbackData.writeBytes(data, 0, data.bytesAvailable);      
    }
    
    private function mixSecondaryBuffers(position: int): void {           
      for each (var buffer: IAudioBuffer in buffers) {
        var samples: ByteArray = buffer.readAudio(position, bufferSize, audioFormat);                
        data = mixFloates(data, samples);                
      }
    }    
    
    private function mixFloates(source: ByteArray, mixin: ByteArray): ByteArray {
      source.position = 0;
      mixin.position = 0;
      var out: ByteArray = new ByteArray();
      while (source.bytesAvailable) {
        var sourceLeft: Number = source.readFloat();
        var sourceRight: Number = source.readFloat();
        var mixinLeft: Number = mixin.readFloat();
        var mixinRight: Number = mixin.readFloat();
        out.writeFloat(sourceLeft / 2 + mixinLeft / 2); 
        out.writeFloat(sourceRight / 2 + mixinRight / 2);
      }
      out.position = 0;
      return out;
    } 
    
    public function addBuffer(buffer: IAudioBuffer): void {
      buffers.push(buffer);
    }        
  }
}